From b760963cdd0c179cb8a2da81803ecd08b270b4e0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C3=89tienne=20Mollier?= Date: Thu, 4 Jun 2026 22:10:06 +0200 Subject: [PATCH] Revert "CVE-2026-10528-partial.patch: new: fix needed by orthanc." The change is causing an ABI breakage that is going to require a transition. It is more appropriate to wait for the 3.7.1 release and coordinate the transition on a sane basis. In the meantime the change is undone. This reverts commit 5d6c0649ddf534d574f7c64245f13a9924400245. --- debian/patches/CVE-2026-10528-partial.patch | 1358 ------------------- debian/patches/series | 1 - 2 files changed, 1359 deletions(-) delete mode 100644 debian/patches/CVE-2026-10528-partial.patch diff --git a/debian/patches/CVE-2026-10528-partial.patch b/debian/patches/CVE-2026-10528-partial.patch deleted file mode 100644 index 29a10891..00000000 --- a/debian/patches/CVE-2026-10528-partial.patch +++ /dev/null @@ -1,1358 +0,0 @@ -Description: Add sequence nesting depth limit for parsing - Track sequence nesting depth on DcmInputStream during parsing. When the - depth exceeds a configurable limit, parsing is aborted with - EC_NestingDepthLimitExceeded. This prevents stack overflow from - excessively nested DICOM sequences. - . - The default limit is controlled by the compile-time macro - DCMTK_MAX_SEQUENCE_NESTING (default: 64). It can be overridden per parse - operation via the runtime API: - . - - DcmInputStream::setMaxNestingDepth() for direct stream access - - DcmItem::setMaxNestingDepth() (inherited by DcmDataset) for loadFile() - and read() - - DcmFileFormat::setMaxNestingDepth() for loadFile() and read(), - forwarded to the contained dataset - - DcmSCP::setMaxNestingDepth() and DcmSCU::setMaxNestingDepth() for - datasets received over the network via receiveDIMSEDataset() - . - The runtime setter uses Sint32 semantics: - 0 = compile-time default, -1 = unlimited, >0 = limit - . - Note: the specific EC_NestingDepthLimitExceeded error code is not yet - surfaced through the DIMSE layer; DIMSE_receiveDataSetInMemory() maps - all parse errors to DIMSE_RECEIVEFAILED. - . - Thanks to the IN-CYPHER OSS Security Team for the report, detailed - analysis and proof of concept. - . - This closes DCMTK Bug #1191. -Author: Michael Onken -Applied-Upstream: 885ff0f10372bd589b5f44cea974f28a3964cb0f -Last-Update: 2026-04-11 -Bug: https://support.dcmtk.org/redmine/issues/1191 -Bug-Debian: https://bugs.debian.org/1138713 -Reviewed-By: Étienne Mollier - ---- dcmtk.orig/config/docs/macros.txt -+++ dcmtk/config/docs/macros.txt -@@ -109,6 +109,21 @@ - dcmtk::log4cplus::threadCleanup() should be called by the user code in - order to clean-up oflog's thread local storage. - -+DCMTK_MAX_SEQUENCE_NESTING -+ Affected: dcmdata -+ Type of modification: Compile-time tunable -+ Explanation: Defines the default maximum permitted sequence nesting depth -+ during DICOM parsing. Deeply nested sequences can cause a stack overflow -+ by exhausting the call stack through unbounded recursion. When this macro -+ is not defined, the default limit of 64 nested sequence levels is used. -+ Real-world DICOM data rarely exceeds 5-10 nesting levels. The limit can -+ be changed at runtime per parse operation via -+ DcmInputStream::setMaxNestingDepth(), DcmItem::setMaxNestingDepth(), or -+ DcmFileFormat::setMaxNestingDepth(). Setting the runtime value to -1 -+ disables the check entirely. -+ Minimum value: 1. Values less than 1 cause a compile-time error. -+ Maximum value: 2147483647 (aligned to 32-bit signed integer runtime API). -+ - DCMTK_MERGE_STDERR_TO_STDOUT - Affected: dcmdata - Type of modification: Activates feature ---- dcmtk.orig/dcmdata/include/dcmtk/dcmdata/dcerror.h -+++ dcmtk/dcmdata/include/dcmtk/dcmdata/dcerror.h -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2025, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -200,6 +200,8 @@ - extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_UnsupportedURIType; - /// Execution of command line failed - extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_CommandLineFailed; -+/// Maximum sequence nesting depth exceeded (stack overflow protection) -+extern DCMTK_DCMDATA_EXPORT const OFConditionConst EC_NestingDepthLimitExceeded; - - ///@} - ---- dcmtk.orig/dcmdata/include/dcmtk/dcmdata/dcfilefo.h -+++ dcmtk/dcmdata/include/dcmtk/dcmdata/dcfilefo.h -@@ -514,6 +514,35 @@ - - /// implementation version name to write in the meta-header - OFString ImplementationVersionName; -+ -+ /// maximum sequence nesting depth for parsing -+ /// (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -1 = unlimited) -+ Sint32 MaxNestingDepth; -+ -+ public: -+ -+ /** set the maximum permitted sequence nesting depth for parsing. -+ * Applied to the input stream in loadFile() and read(), and also -+ * forwarded to the contained dataset. -+ * - Value 0 (default): apply the compile-time default -+ * (DCMTK_MAX_SEQUENCE_NESTING, default is 64) -+ * - Value -1: disable the check (allow unlimited nesting) -+ * - Value > 0: use this value as the maximum permitted nesting depth -+ * @param maxDepth maximum nesting depth setting -+ */ -+ void setMaxNestingDepth(Sint32 maxDepth) -+ { -+ MaxNestingDepth = maxDepth; -+ DcmDataset* dset = getDataset(); -+ if (dset) -+ dset->setMaxNestingDepth(maxDepth); -+ } -+ -+ /** return the maximum permitted sequence nesting depth for parsing. -+ * @return maximum nesting depth setting -+ * (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -1 = unlimited) -+ */ -+ Sint32 getMaxNestingDepth() const { return MaxNestingDepth; } - }; - - ---- dcmtk.orig/dcmdata/include/dcmtk/dcmdata/dcistrma.h -+++ dcmtk/dcmdata/include/dcmtk/dcmdata/dcistrma.h -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2018, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -224,6 +224,40 @@ - */ - virtual DcmInputStreamFactory *newFactory() const = 0; - -+ /** returns the current sequence nesting depth. -+ * This counter is used to prevent stack overflow from deeply nested -+ * DICOM sequences in malicious input files. -+ * @return current nesting depth -+ */ -+ Uint32 nestingDepth() const; -+ -+ /** increments the sequence nesting depth counter. -+ * @return the new nesting depth after incrementing -+ */ -+ Uint32 incrementNestingDepth(); -+ -+ /** decrements the sequence nesting depth counter. -+ * Does nothing if the counter is already zero. -+ */ -+ void decrementNestingDepth(); -+ -+ /** returns the maximum permitted sequence nesting depth for this stream. -+ * A value of 0 means the compile-time default (DCMTK_MAX_SEQUENCE_NESTING) applies. -+ * A value of -1 means the check is disabled (unlimited nesting). -+ * @return maximum nesting depth setting -+ */ -+ Sint32 maxNestingDepth() const; -+ -+ /** sets the maximum permitted sequence nesting depth for this stream. -+ * Must be called before parsing begins. -+ * - Value 0 (default): apply the compile-time default -+ * (DCMTK_MAX_SEQUENCE_NESTING, default is 64) -+ * - Value -1: disable the check (allow unlimited nesting) -+ * - Value > 0: use this value as the maximum permitted nesting depth -+ * @param maxDepth maximum nesting depth setting -+ */ -+ void setMaxNestingDepth(Sint32 maxDepth); -+ - /** marks the current stream position for a later putback operation, - * overwriting a possibly existing prior putback mark. - * The DcmObject read methods rely on the possibility to putback -@@ -272,6 +306,12 @@ - - /// putback marker - offile_off_t mark_; -+ -+ /// current sequence nesting depth (for stack overflow protection) -+ Uint32 nestingDepth_; -+ -+ /// maximum permitted sequence nesting depth (0 = default 64, -1 = unlimited) -+ Sint32 maxNestingDepth_; - }; - - ---- dcmtk.orig/dcmdata/include/dcmtk/dcmdata/dcitem.h -+++ dcmtk/dcmdata/include/dcmtk/dcmdata/dcitem.h -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2025, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -1573,6 +1573,29 @@ - - /// cache for private creator tags and identifiers - DcmPrivateTagCache privateCreatorCache; -+ -+ /// maximum sequence nesting depth for parsing -+ /// (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -1 = unlimited) -+ Sint32 maxNestingDepth; -+ -+ public: -+ -+ /** set the maximum permitted sequence nesting depth for parsing. -+ * This limit is applied to the input stream before parsing in -+ * loadFile() and read(). -+ * - Value 0 (default): apply the compile-time default -+ * (DCMTK_MAX_SEQUENCE_NESTING, default is 64) -+ * - Value -1: disable the check (allow unlimited nesting) -+ * - Value > 0: use this value as the maximum permitted nesting depth -+ * @param maxDepth maximum nesting depth setting -+ */ -+ void setMaxNestingDepth(Sint32 maxDepth) { maxNestingDepth = maxDepth; } -+ -+ /** return the maximum permitted sequence nesting depth for parsing. -+ * @return maximum nesting depth setting -+ * (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -1 = unlimited) -+ */ -+ Sint32 getMaxNestingDepth() const { return maxNestingDepth; } - }; - - /** Checks whether left hand side item is smaller than right hand side ---- dcmtk.orig/dcmdata/include/dcmtk/dcmdata/dcobject.h -+++ dcmtk/dcmdata/include/dcmtk/dcmdata/dcobject.h -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2024, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -50,6 +50,18 @@ - - // Undefined Length Identifier now defined in dctypes.h - -+// Default maximum sequence nesting depth (can be overridden at compile time). -+// Must be in the range [1, 2147483647]. -+#ifndef DCMTK_MAX_SEQUENCE_NESTING -+#define DCMTK_MAX_SEQUENCE_NESTING 64 -+#endif -+#if DCMTK_MAX_SEQUENCE_NESTING < 1 -+#error "DCMTK_MAX_SEQUENCE_NESTING must be >= 1" -+#endif -+#if DCMTK_MAX_SEQUENCE_NESTING > 2147483647 -+#error "DCMTK_MAX_SEQUENCE_NESTING must be <= 2147483647" -+#endif -+ - // Maximum number of read bytes for a Value Element - const Uint32 DCM_MaxReadLength = 4096; - ---- dcmtk.orig/dcmdata/libsrc/dcdatset.cc -+++ dcmtk/dcmdata/libsrc/dcdatset.cc -@@ -647,6 +647,9 @@ - { - /* use stdin stream */ - DcmStdinStream inStream; -+ /* apply configured nesting depth limit */ -+ if (getMaxNestingDepth() > 0) -+ inStream.setMaxNestingDepth(getMaxNestingDepth()); - - /* clear this object */ - l_error = clear(); -@@ -670,6 +673,9 @@ - } else { - /* open file for input */ - DcmInputFileStream fileStream(fileName); -+ /* apply configured nesting depth limit */ -+ if (getMaxNestingDepth() > 0) -+ fileStream.setMaxNestingDepth(getMaxNestingDepth()); - - /* check stream status */ - l_error = fileStream.status(); ---- dcmtk.orig/dcmdata/libsrc/dcerror.cc -+++ dcmtk/dcmdata/libsrc/dcerror.cc -@@ -91,6 +91,7 @@ - makeOFConditionConst(EC_BulkDataURINotSupported, OFM_dcmdata, 66, OF_error, "BulkDataURI not yet supported" ); - makeOFConditionConst(EC_UnsupportedURIType, OFM_dcmdata, 67, OF_error, "Unsupported URI type" ); - makeOFConditionConst(EC_CommandLineFailed, OFM_dcmdata, 68, OF_error, "Execution of command line failed" ); -+makeOFConditionConst(EC_NestingDepthLimitExceeded, OFM_dcmdata, 69, OF_error, "Maximum sequence nesting depth exceeded" ); - - const unsigned short EC_CODE_CannotSelectCharacterSet = 35; - const unsigned short EC_CODE_CannotConvertCharacterSet = 36; ---- dcmtk.orig/dcmdata/libsrc/dcfilefo.cc -+++ dcmtk/dcmdata/libsrc/dcfilefo.cc -@@ -54,7 +54,8 @@ - : DcmSequenceOfItems(DCM_InternalUseTag), - FileReadMode(ERM_autoDetect), - ImplementationClassUID(OFFIS_IMPLEMENTATION_CLASS_UID), -- ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) -+ ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME), -+ MaxNestingDepth(0) - { - DcmMetaInfo *MetaInfo = new DcmMetaInfo(); - DcmSequenceOfItems::itemList->insert(MetaInfo); -@@ -71,7 +72,8 @@ - : DcmSequenceOfItems(DCM_InternalUseTag), - FileReadMode(ERM_autoDetect), - ImplementationClassUID(OFFIS_IMPLEMENTATION_CLASS_UID), -- ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME) -+ ImplementationVersionName(OFFIS_DTK_IMPLEMENTATION_VERSION_NAME), -+ MaxNestingDepth(0) - { - DcmMetaInfo *MetaInfo = new DcmMetaInfo(); - DcmSequenceOfItems::itemList->insert(MetaInfo); -@@ -99,7 +101,8 @@ - : DcmSequenceOfItems(old), - FileReadMode(old.FileReadMode), - ImplementationClassUID(old.ImplementationClassUID), -- ImplementationVersionName(old.ImplementationVersionName) -+ ImplementationVersionName(old.ImplementationVersionName), -+ MaxNestingDepth(old.MaxNestingDepth) - { - } - -@@ -128,6 +131,7 @@ - FileReadMode = obj.FileReadMode; - ImplementationClassUID = obj.ImplementationClassUID; - ImplementationVersionName = obj.ImplementationVersionName; -+ MaxNestingDepth = obj.MaxNestingDepth; - } - - return *this; -@@ -942,6 +946,9 @@ - { - /* use stdin stream */ - DcmStdinStream inStream; -+ /* apply configured nesting depth limit */ -+ if (MaxNestingDepth != 0) -+ inStream.setMaxNestingDepth(MaxNestingDepth); - - /* clear this object */ - l_error = clear(); -@@ -972,6 +979,9 @@ - } else { - /* open file for output */ - DcmInputFileStream fileStream(fileName); -+ /* apply configured nesting depth limit */ -+ if (MaxNestingDepth > 0) -+ fileStream.setMaxNestingDepth(MaxNestingDepth); - - /* check stream status */ - l_error = fileStream.status(); ---- dcmtk.orig/dcmdata/libsrc/dcistrma.cc -+++ dcmtk/dcmdata/libsrc/dcistrma.cc -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2010, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -29,6 +29,8 @@ - , compressionFilter_(NULL) - , tell_(0) - , mark_(0) -+, nestingDepth_(0) -+, maxNestingDepth_(0) - { - } - -@@ -94,6 +96,32 @@ - return current_; - } - -+Uint32 DcmInputStream::nestingDepth() const -+{ -+ return nestingDepth_; -+} -+ -+Uint32 DcmInputStream::incrementNestingDepth() -+{ -+ return ++nestingDepth_; -+} -+ -+void DcmInputStream::decrementNestingDepth() -+{ -+ if (nestingDepth_ > 0) -+ --nestingDepth_; -+} -+ -+Sint32 DcmInputStream::maxNestingDepth() const -+{ -+ return maxNestingDepth_; -+} -+ -+void DcmInputStream::setMaxNestingDepth(Sint32 maxDepth) -+{ -+ maxNestingDepth_ = maxDepth; -+} -+ - OFCondition DcmInputStream::installCompressionFilter(E_StreamCompression filterType) - { - OFCondition result = EC_Normal; ---- dcmtk.orig/dcmdata/libsrc/dcitem.cc -+++ dcmtk/dcmdata/libsrc/dcitem.cc -@@ -83,7 +83,8 @@ - elementList(NULL), - lastElementComplete(OFTrue), - fStartPosition(0), -- privateCreatorCache() -+ privateCreatorCache(), -+ maxNestingDepth(0) - { - elementList = new DcmList; - } -@@ -95,7 +96,8 @@ - elementList(NULL), - lastElementComplete(OFTrue), - fStartPosition(0), -- privateCreatorCache() -+ privateCreatorCache(), -+ maxNestingDepth(0) - { - elementList = new DcmList; - } -@@ -106,7 +108,8 @@ - elementList(new DcmList), - lastElementComplete(old.lastElementComplete), - fStartPosition(old.fStartPosition), -- privateCreatorCache() -+ privateCreatorCache(), -+ maxNestingDepth(old.maxNestingDepth) - { - if (!old.elementList->empty()) - { -@@ -136,6 +139,7 @@ - // copy DcmItem's member variables - lastElementComplete = obj.lastElementComplete; - fStartPosition = obj.fStartPosition; -+ maxNestingDepth = obj.maxNestingDepth; - if (!obj.elementList->empty()) - { - elementList->seek(ELP_first); -@@ -1394,6 +1398,10 @@ - return errorFlag; - } - -+ /* apply configured nesting depth limit to the input stream (0 = use default, skip override) */ -+ if (getMaxNestingDepth() != 0) -+ inStream.setMaxNestingDepth(getMaxNestingDepth()); -+ - /* figure out if the stream reported an error */ - errorFlag = inStream.status(); - /* if the stream reported an error or if it is the end of the */ ---- dcmtk.orig/dcmdata/libsrc/dcsequen.cc -+++ dcmtk/dcmdata/libsrc/dcsequen.cc -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2023, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -704,6 +704,23 @@ - errorFlag = EC_IllegalCall; - else - { -+ const Uint32 depth = inStream.incrementNestingDepth(); -+ const Sint32 maxDepthSetting = inStream.maxNestingDepth(); -+ /* -1 = unlimited; 0 = use built-in default (DCMTK_MAX_SEQUENCE_NESTING); > 0 = custom limit */ -+ const Uint32 effectiveMax = (maxDepthSetting == 0) ? DCMTK_MAX_SEQUENCE_NESTING -+ : (maxDepthSetting < 0) ? 0 /* unlimited */ -+ : OFstatic_cast(Uint32, maxDepthSetting); -+ if (effectiveMax > 0 && depth > effectiveMax) -+ { -+ DCMDATA_ERROR("DcmSequenceOfItems: Maximum nesting depth (" << effectiveMax -+ << ") exceeded while parsing sequence " << getTagName() << " " << getTag()); -+ inStream.decrementNestingDepth(); -+ errorFlag = EC_NestingDepthLimitExceeded; -+ // dump information if required -+ DCMDATA_TRACE("DcmSequenceOfItems::read() returns error = " << errorFlag.text()); -+ return errorFlag; -+ } -+ - errorFlag = inStream.status(); - - if (errorFlag.good() && inStream.eos()) -@@ -773,6 +790,7 @@ - errorFlag = EC_Normal; - if (errorFlag.good()) - setTransferState(ERW_ready); // sequence is complete -+ inStream.decrementNestingDepth(); - } - // dump information if required - DCMDATA_TRACE("DcmSequenceOfItems::read() returns error = " << errorFlag.text()); ---- dcmtk.orig/dcmdata/tests/CMakeLists.txt -+++ dcmtk/dcmdata/tests/CMakeLists.txt -@@ -11,6 +11,7 @@ - ti2dbmp.cc - titem.cc - tmatch.cc -+ tnesting.cc - tnewdcme.cc - tparent.cc - tparser.cc ---- dcmtk.orig/dcmdata/tests/Makefile.dep -+++ dcmtk/dcmdata/tests/Makefile.dep -@@ -712,6 +712,73 @@ - ../include/dcmtk/dcmdata/dcvr.h \ - ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ - ../include/dcmtk/dcmdata/dcmatch.h -+tnesting.o: tnesting.cc ../../config/include/dcmtk/config/osconfig.h \ -+ ../../ofstd/include/dcmtk/ofstd/oftest.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ -+ ../../ofstd/include/dcmtk/ofstd/oftypes.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofdefine.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofcast.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofexport.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofstdinc.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofcmdln.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofexbl.h \ -+ ../../ofstd/include/dcmtk/ofstd/oftraits.h \ -+ ../../ofstd/include/dcmtk/ofstd/oflist.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofstring.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofstream.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofconsol.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofthread.h \ -+ ../../ofstd/include/dcmtk/ofstd/offile.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofstd.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofcond.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofdiag.h \ -+ ../../ofstd/include/dcmtk/ofstd/diag/push.def \ -+ ../../ofstd/include/dcmtk/ofstd/diag/useafree.def \ -+ ../../ofstd/include/dcmtk/ofstd/diag/pop.def \ -+ ../../ofstd/include/dcmtk/ofstd/oflimits.h \ -+ ../../ofstd/include/dcmtk/ofstd/oferror.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofexit.h \ -+ ../include/dcmtk/dcmdata/dcuid.h ../include/dcmtk/dcmdata/dcdefine.h \ -+ ../../oflog/include/dcmtk/oflog/oflog.h \ -+ ../../oflog/include/dcmtk/oflog/logger.h \ -+ ../../oflog/include/dcmtk/oflog/config.h \ -+ ../../oflog/include/dcmtk/oflog/config/defines.h \ -+ ../../oflog/include/dcmtk/oflog/helpers/threadcf.h \ -+ ../../oflog/include/dcmtk/oflog/loglevel.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofvector.h \ -+ ../../oflog/include/dcmtk/oflog/tstring.h \ -+ ../../oflog/include/dcmtk/oflog/tchar.h \ -+ ../../oflog/include/dcmtk/oflog/spi/apndatch.h \ -+ ../../oflog/include/dcmtk/oflog/appender.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofmem.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofutil.h \ -+ ../../ofstd/include/dcmtk/ofstd/variadic/tuplefwd.h \ -+ ../../oflog/include/dcmtk/oflog/layout.h \ -+ ../../oflog/include/dcmtk/oflog/streams.h \ -+ ../../oflog/include/dcmtk/oflog/helpers/pointer.h \ -+ ../../oflog/include/dcmtk/oflog/thread/syncprim.h \ -+ ../../oflog/include/dcmtk/oflog/spi/filter.h \ -+ ../../oflog/include/dcmtk/oflog/helpers/lockfile.h \ -+ ../../oflog/include/dcmtk/oflog/spi/logfact.h \ -+ ../../oflog/include/dcmtk/oflog/logmacro.h \ -+ ../../oflog/include/dcmtk/oflog/helpers/snprintf.h \ -+ ../../oflog/include/dcmtk/oflog/tracelog.h \ -+ ../../ofstd/include/dcmtk/ofstd/oftempf.h \ -+ ../include/dcmtk/dcmdata/dcdatset.h ../include/dcmtk/dcmdata/dcitem.h \ -+ ../include/dcmtk/dcmdata/dctypes.h ../include/dcmtk/dcmdata/dcobject.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofglobal.h \ -+ ../include/dcmtk/dcmdata/dcerror.h ../include/dcmtk/dcmdata/dcxfer.h \ -+ ../include/dcmtk/dcmdata/dcvr.h \ -+ ../../ofstd/include/dcmtk/ofstd/ofdeprec.h \ -+ ../include/dcmtk/dcmdata/dctag.h ../include/dcmtk/dcmdata/dctagkey.h \ -+ ../../ofstd/include/dcmtk/ofstd/diag/ignrattr.def \ -+ ../include/dcmtk/dcmdata/dcstack.h ../include/dcmtk/dcmdata/dclist.h \ -+ ../include/dcmtk/dcmdata/dcpcache.h ../include/dcmtk/dcmdata/dcfilefo.h \ -+ ../include/dcmtk/dcmdata/dcsequen.h ../include/dcmtk/dcmdata/dcelem.h \ -+ ../include/dcmtk/dcmdata/dcistrmb.h ../include/dcmtk/dcmdata/dcistrma.h \ -+ ../include/dcmtk/dcmdata/dcostrmb.h ../include/dcmtk/dcmdata/dcostrma.h \ -+ ../include/dcmtk/dcmdata/dcdeftag.h ../include/dcmtk/dcmdata/dcwcache.h \ -+ ../include/dcmtk/dcmdata/dcfcache.h - tnewdcme.o: tnewdcme.cc ../../config/include/dcmtk/config/osconfig.h \ - ../../ofstd/include/dcmtk/ofstd/oftest.h \ - ../../ofstd/include/dcmtk/ofstd/ofconapp.h \ ---- dcmtk.orig/dcmdata/tests/Makefile.in -+++ dcmtk/dcmdata/tests/Makefile.in -@@ -25,7 +25,7 @@ - - objs = tests.o tpread.o ti2dbmp.o tchval.o tpath.o tvrdatim.o telemlen.o tparser.o \ - tdict.o tvrds.o tvrfd.o tvrui.o tvrol.o tvrov.o tvrsv.o tvruv.o tstrval.o \ -- tspchrs.o tvrpn.o tparent.o tfilter.o tvrcomp.o tmatch.o tnewdcme.o \ -+ tspchrs.o tvrpn.o tparent.o tfilter.o tvrcomp.o tmatch.o tnesting.o tnewdcme.o \ - tgenuid.o tsequen.o titem.o ttag.o txfer.o tbytestr.o tfrmsiz.o - - progs = tests ---- dcmtk.orig/dcmdata/tests/tests.cc -+++ dcmtk/dcmdata/tests/tests.cc -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 2011-2025 OFFIS e.V. -+ * Copyright (C) 2011-2026 OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were developed by -@@ -127,5 +127,12 @@ - OFTEST_REGISTER(dcmdata_xferLookup_4); - OFTEST_REGISTER(dcmdata_putOFStringAtPos); - OFTEST_REGISTER(dcmdata_uncompressedFrameSize); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_exceeded); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_atLimit); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_wellBelow); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_customLimit); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_disabled); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_datasetAPI); -+OFTEST_REGISTER(dcmdata_nestingDepthLimit_fileFormatAPI); - - OFTEST_MAIN("dcmdata") ---- /dev/null -+++ dcmtk/dcmdata/tests/tnesting.cc -@@ -0,0 +1,266 @@ -+/* -+ * -+ * Copyright (C) 2026, OFFIS e.V. -+ * All rights reserved. See COPYRIGHT file for details. -+ * -+ * This software and supporting documentation were developed by -+ * -+ * OFFIS e.V. -+ * R&D Division Health -+ * Escherweg 2 -+ * D-26121 Oldenburg, Germany -+ * -+ * -+ * Module: dcmdata -+ * -+ * Author: Michael Onken -+ * -+ * Purpose: test program for sequence nesting depth limit -+ * -+ */ -+ -+ -+#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ -+ -+#include "dcmtk/ofstd/oftest.h" -+#include "dcmtk/ofstd/oftempf.h" -+#include "dcmtk/dcmdata/dcdatset.h" -+#include "dcmtk/dcmdata/dcfilefo.h" -+#include "dcmtk/dcmdata/dcsequen.h" -+#include "dcmtk/dcmdata/dcistrmb.h" -+#include "dcmtk/dcmdata/dcostrmb.h" -+#include "dcmtk/dcmdata/dcerror.h" -+#include "dcmtk/dcmdata/dcdeftag.h" -+#include "dcmtk/dcmdata/dcwcache.h" -+ -+ -+/** Helper: build a DcmDataset containing a chain of nested sequences to the -+ * specified depth using the high-level dcmdata API. Each nesting level -+ * consists of a DcmSequenceOfItems with one DcmItem, and the innermost -+ * item is empty. -+ * @param depth the number of nested sequence levels to generate -+ * @param dset output dataset (cleared before use) -+ */ -+static void buildNestedDataset(Uint32 depth, DcmDataset& dset) -+{ -+ dset.clear(); -+ /* start with the dataset as the outermost item */ -+ DcmItem* currentItem = &dset; -+ for (Uint32 i = 0; i < depth; ++i) -+ { -+ DcmSequenceOfItems* seq = new DcmSequenceOfItems(DCM_ReferencedSeriesSequence); -+ currentItem->insert(seq); -+ DcmItem* item = new DcmItem(); -+ seq->insert(item); -+ currentItem = item; -+ } -+} -+ -+ -+/** Helper: serialize a dataset to a byte buffer and parse it back using a -+ * fresh DcmInputStream, with an optional custom nesting depth limit. -+ * @param srcDset the dataset to serialize -+ * @param dstDset output dataset to receive the parsed result -+ * @param maxNestingDepth nesting depth setting passed to setMaxNestingDepth(): -+ * 0 (default) = compile-time default (DCMTK_MAX_SEQUENCE_NESTING), -1 = unlimited, > 0 = custom limit -+ * @return the OFCondition returned by DcmDataset::read() -+ */ -+static OFCondition serializeAndParse(DcmDataset& srcDset, DcmDataset& dstDset, -+ Sint32 maxNestingDepth = 0) -+{ -+ /* write the dataset to a memory buffer */ -+ const size_t bufLen = 1024 * 1024; -+ Uint8* buf = new Uint8[bufLen]; -+ -+ DcmOutputBufferStream outStream(buf, bufLen); -+ srcDset.transferInit(); -+ DcmWriteCache wcache; -+ OFCondition cond = srcDset.write(outStream, EXS_LittleEndianExplicit, EET_UndefinedLength, &wcache); -+ srcDset.transferEnd(); -+ if (cond.bad()) -+ { -+ delete[] buf; -+ return cond; -+ } -+ offile_off_t bytesWritten = 0; -+ void* writtenBuf = NULL; -+ outStream.flushBuffer(writtenBuf, bytesWritten); -+ -+ /* parse it back with nesting depth limit */ -+ DcmInputBufferStream inStream; -+ inStream.setBuffer(buf, bytesWritten); -+ inStream.setEos(); -+ inStream.setMaxNestingDepth(maxNestingDepth); -+ -+ dstDset.clear(); -+ dstDset.transferInit(); -+ cond = dstDset.read(inStream, EXS_LittleEndianExplicit); -+ dstDset.transferEnd(); -+ -+ delete[] buf; -+ return cond; -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_exceeded) -+{ -+ /* One level beyond the default limit (DCMTK_MAX_SEQUENCE_NESTING) must be rejected. */ -+ DcmDataset srcDset, dstDset; -+ buildNestedDataset(DCMTK_MAX_SEQUENCE_NESTING + 1, srcDset); -+ -+ OFCondition cond = serializeAndParse(srcDset, dstDset); -+ if (cond != EC_NestingDepthLimitExceeded) -+ { -+ OFCHECK_FAIL("Expected EC_NestingDepthLimitExceeded for " << (DCMTK_MAX_SEQUENCE_NESTING + 1) -+ << " levels of nesting, but got: " << cond.text()); -+ } -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_atLimit) -+{ -+ /* Exactly DCMTK_MAX_SEQUENCE_NESTING levels must still pass. */ -+ DcmDataset srcDset, dstDset; -+ buildNestedDataset(DCMTK_MAX_SEQUENCE_NESTING, srcDset); -+ -+ OFCondition cond = serializeAndParse(srcDset, dstDset); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("Parsing " << DCMTK_MAX_SEQUENCE_NESTING -+ << " levels of nesting should succeed, but got: " << cond.text()); -+ } -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_wellBelow) -+{ -+ /* 5 levels of nesting (typical real-world depth) must parse fine. */ -+ DcmDataset srcDset, dstDset; -+ buildNestedDataset(5, srcDset); -+ -+ OFCondition cond = serializeAndParse(srcDset, dstDset); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("Parsing 5 levels of nesting should succeed, but got: " << cond.text()); -+ } -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_customLimit) -+{ -+ /* Test setMaxNestingDepth(): set the limit to 10. -+ * 10 levels must succeed, 11 levels must fail. */ -+ DcmDataset srcDset, dstDset; -+ OFCondition cond; -+ -+ buildNestedDataset(10, srcDset); -+ cond = serializeAndParse(srcDset, dstDset, 10); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("Parsing 10 levels with maxNestingDepth=10 should succeed, but got: " << cond.text()); -+ } -+ -+ buildNestedDataset(11, srcDset); -+ cond = serializeAndParse(srcDset, dstDset, 10); -+ if (cond != EC_NestingDepthLimitExceeded) -+ { -+ OFCHECK_FAIL("Expected EC_NestingDepthLimitExceeded for 11 levels with maxNestingDepth=10, but got: " << cond.text()); -+ } -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_disabled) -+{ -+ /* Test that setMaxNestingDepth(-1) disables the limit entirely. -+ * Even 200 levels of nesting must succeed. */ -+ DcmDataset srcDset, dstDset; -+ buildNestedDataset(200, srcDset); -+ -+ OFCondition cond = serializeAndParse(srcDset, dstDset, -1); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("Parsing 200 levels with maxNestingDepth=-1 (unlimited) should succeed, but got: " << cond.text()); -+ } -+} -+ -+ -+/** Helper: create a temporary file path. -+ * @param tmpFile receives the temporary file path -+ * @return EC_Normal if successful -+ */ -+static OFCondition makeTempFile(OFString& tmpFile) -+{ -+ return OFTempFile::createFile(tmpFile, NULL /* fd_out */, O_RDWR, -+ "" /* dir */, "" /* prefix */, ".dcm" /* postfix */); -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_datasetAPI) -+{ -+ /* Test DcmDataset::setMaxNestingDepth() with loadFile(). */ -+ DcmDataset srcDset; -+ OFString tmpFile; -+ OFCondition cond; -+ -+ /* save as raw dataset (no meta header) */ -+ buildNestedDataset(11, srcDset); -+ cond = makeTempFile(tmpFile); -+ OFCHECK(cond.good()); -+ cond = srcDset.saveFile(tmpFile, EXS_LittleEndianExplicit); -+ OFCHECK(cond.good()); -+ -+ /* default limit (DCMTK_MAX_SEQUENCE_NESTING): 11 levels should succeed */ -+ DcmDataset dset1; -+ cond = dset1.loadFile(tmpFile, EXS_LittleEndianExplicit); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("DcmDataset::loadFile() with 11 levels should succeed with default limit, but got: " << cond.text()); -+ } -+ -+ /* custom limit 10: 11 levels should fail */ -+ DcmDataset dset2; -+ dset2.setMaxNestingDepth(10); -+ cond = dset2.loadFile(tmpFile, EXS_LittleEndianExplicit); -+ if (cond != EC_NestingDepthLimitExceeded) -+ { -+ OFCHECK_FAIL("DcmDataset::loadFile() with 11 levels and maxNestingDepth=10 should fail, but got: " << cond.text()); -+ } -+ -+ OFStandard::deleteFile(tmpFile); -+} -+ -+ -+OFTEST(dcmdata_nestingDepthLimit_fileFormatAPI) -+{ -+ /* Test DcmFileFormat::setMaxNestingDepth() with loadFile(). */ -+ DcmDataset srcDset; -+ OFString tmpFile; -+ OFCondition cond; -+ -+ /* save as DcmFileFormat (with meta header) */ -+ buildNestedDataset(11, srcDset); -+ cond = makeTempFile(tmpFile); -+ OFCHECK(cond.good()); -+ DcmFileFormat srcFF(&srcDset); -+ cond = srcFF.saveFile(tmpFile, EXS_LittleEndianExplicit); -+ OFCHECK(cond.good()); -+ -+ /* default limit (DCMTK_MAX_SEQUENCE_NESTING): 11 levels should succeed */ -+ DcmFileFormat ff1; -+ cond = ff1.loadFile(tmpFile); -+ if (cond.bad()) -+ { -+ OFCHECK_FAIL("DcmFileFormat::loadFile() with 11 levels should succeed with default limit, but got: " << cond.text()); -+ } -+ -+ /* custom limit 10: 11 levels should fail */ -+ DcmFileFormat ff2; -+ ff2.setMaxNestingDepth(10); -+ cond = ff2.loadFile(tmpFile); -+ if (cond != EC_NestingDepthLimitExceeded) -+ { -+ OFCHECK_FAIL("DcmFileFormat::loadFile() with 11 levels and maxNestingDepth=10 should fail, but got: " << cond.text()); -+ } -+ -+ OFStandard::deleteFile(tmpFile); -+} ---- dcmtk.orig/dcmnet/include/dcmtk/dcmnet/scp.h -+++ dcmtk/dcmnet/include/dcmtk/dcmnet/scp.h -@@ -360,6 +360,17 @@ - */ - void setAlwaysAcceptDefaultRole(const OFBool enabled); - -+ /** Set the maximum permitted sequence nesting depth for parsing -+ * datasets received over the network. This limit is applied to -+ * each dataset received via receiveDIMSEDataset(). -+ * - Value 0 (default): apply the compile-time default -+ * (DCMTK_MAX_SEQUENCE_NESTING) -+ * - Value -1: disable the check (allow unlimited nesting) -+ * - Value > 0: use this value as the maximum nesting depth -+ * @param maxDepth maximum nesting depth setting -+ */ -+ void setMaxNestingDepth(const Sint32 maxDepth); -+ - /* Get methods for SCP settings */ - - /** Returns TCP/IP port number SCP listens for new connection requests -@@ -434,6 +445,14 @@ - */ - OFBool getProgressNotificationMode() const; - -+ /** Return the maximum permitted sequence nesting depth for -+ * parsing datasets received over the network. -+ * @return maximum nesting depth setting -+ * (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -+ * -1 = unlimited) -+ */ -+ Sint32 getMaxNestingDepth() const; -+ - /** Get access to the configuration of the SCP. Note that the functionality - * on the configuration object is shadowed by other API functions of DcmSCP. - * The existing functions are provided in order to not break users of this -@@ -1157,6 +1176,11 @@ - /// it, e.g. in the context of the DcmSCPPool class. - DcmSharedSCPConfig m_cfg; - -+ /// Maximum sequence nesting depth for parsing received datasets -+ /// (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -+ /// -1 = unlimited) -+ Sint32 m_maxNestingDepth; -+ - /** Drops association and clears internal structures to free memory - */ - void dropAndDestroyAssociation(); ---- dcmtk.orig/dcmnet/include/dcmtk/dcmnet/scu.h -+++ dcmtk/dcmnet/include/dcmtk/dcmnet/scu.h -@@ -739,6 +739,17 @@ - */ - void setProgressNotificationMode(const OFBool mode); - -+ /** Set the maximum permitted sequence nesting depth for parsing -+ * datasets received over the network. This limit is applied to -+ * each dataset received via receiveDIMSEDataset(). -+ * - Value 0 (default): apply the compile-time default -+ * (DCMTK_MAX_SEQUENCE_NESTING) -+ * - Value -1: disable the check (allow unlimited nesting) -+ * - Value > 0: use this value as the maximum nesting depth -+ * @param maxDepth maximum nesting depth setting -+ */ -+ void setMaxNestingDepth(const Sint32 maxDepth); -+ - /* Get methods */ - - /** Get current connection status -@@ -831,6 +842,14 @@ - */ - OFBool getProgressNotificationMode() const; - -+ /** Return the maximum permitted sequence nesting depth for -+ * parsing datasets received over the network. -+ * @return maximum nesting depth setting -+ * (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -+ * -1 = unlimited) -+ */ -+ Sint32 getMaxNestingDepth() const; -+ - /** Returns whether SCU is configured to create a TLS connection with the SCP - * @return OFTrue if TLS mode has been enabled, OFFalse otherwise - */ -@@ -1136,6 +1155,11 @@ - /// Progress notification mode (default: enabled) - OFBool m_progressNotificationMode; - -+ /// Maximum sequence nesting depth for parsing received datasets -+ /// (0 = compile-time default DCMTK_MAX_SEQUENCE_NESTING, -+ /// -1 = unlimited) -+ Sint32 m_maxNestingDepth; -+ - /// Flag indicating whether secure mode has been enabled (default: disabled) - OFBool m_secureConnectionEnabled; - ---- dcmtk.orig/dcmnet/libsrc/dimse.cc -+++ dcmtk/dcmnet/libsrc/dimse.cc -@@ -1,6 +1,6 @@ - /* - * -- * Copyright (C) 1994-2025, OFFIS e.V. -+ * Copyright (C) 1994-2026, OFFIS e.V. - * All rights reserved. See COPYRIGHT file for details. - * - * This software and supporting documentation were partly developed by -@@ -1536,7 +1536,8 @@ - dset = *dataObject; - } - -- /* check if there is still no DcmDataset object which can be used to store the data set. */ -+ /* check if there is still no DcmDataset object (i.e. if allocation fails) */ -+ /* which can be used to store the data set. */ - if (dset == NULL) - { - /* if this is the case, just go ahead an receive data, but do not store it anywhere */ ---- dcmtk.orig/dcmnet/libsrc/scp.cc -+++ dcmtk/dcmnet/libsrc/scp.cc -@@ -32,6 +32,7 @@ - : m_network(NULL) - , m_assoc(NULL) - , m_cfg() -+, m_maxNestingDepth(0) - { - OFStandard::initializeNetwork(); - } -@@ -1590,6 +1591,19 @@ - if (m_assoc == NULL) - return DIMSE_ILLEGALASSOCIATION; - -+ /* if a custom nesting depth limit is configured, pre-allocate -+ * the dataset and apply the setting before parsing starts. -+ * DIMSE_receiveDataSetInMemory() will use this dataset instead -+ * of creating a new one internally. */ -+ OFBool weAllocated = OFFalse; -+ if (dataObject != NULL && *dataObject == NULL -+ && m_maxNestingDepth != 0) -+ { -+ *dataObject = new DcmDataset(); -+ (*dataObject)->setMaxNestingDepth(m_maxNestingDepth); -+ weAllocated = OFTrue; -+ } -+ - OFCondition cond; - /* call the corresponding DIMSE function to receive the dataset */ - if (m_cfg->getProgressNotificationMode()) -@@ -1619,6 +1633,14 @@ - } - else - { -+ /* if we pre-allocated the dataset, clean up on error since -+ * DIMSE_receiveDataSetInMemory() won't delete a dataset -+ * that was passed in by the caller */ -+ if (weAllocated && dataObject != NULL) -+ { -+ delete *dataObject; -+ *dataObject = NULL; -+ } - OFString tempStr; - DCMNET_ERROR("Unable to receive dataset on presentation context " - << OFstatic_cast(unsigned int, *presID) << ": " << DimseCondition::dump(tempStr, cond)); -@@ -1910,6 +1932,13 @@ - - // ---------------------------------------------------------------------------- - -+void DcmSCP::setMaxNestingDepth(const Sint32 maxDepth) -+{ -+ m_maxNestingDepth = maxDepth; -+} -+ -+// ---------------------------------------------------------------------------- -+ - /* Get methods for SCP settings and current association information */ - - OFBool DcmSCP::getRefuseAssociation() const -@@ -2002,6 +2031,13 @@ - } - - // ---------------------------------------------------------------------------- -+ -+Sint32 DcmSCP::getMaxNestingDepth() const -+{ -+ return m_maxNestingDepth; -+} -+ -+// ---------------------------------------------------------------------------- - - OFBool DcmSCP::isConnected() const - { ---- dcmtk.orig/dcmnet/libsrc/scu.cc -+++ dcmtk/dcmnet/libsrc/scu.cc -@@ -56,6 +56,7 @@ - , m_verbosePCMode(OFFalse) - , m_datasetConversionMode(OFFalse) - , m_progressNotificationMode(OFTrue) -+ , m_maxNestingDepth(0) - , m_secureConnectionEnabled(OFFalse) - , m_protocolVersion(ASC_AF_Default) - { -@@ -2500,6 +2501,19 @@ - if (!isConnected()) - return DIMSE_ILLEGALASSOCIATION; - -+ /* if a custom nesting depth limit is configured, pre-allocate -+ * the dataset and apply the setting before parsing starts. -+ * DIMSE_receiveDataSetInMemory() will use this dataset instead -+ * of creating a new one internally. */ -+ OFBool weAllocated = OFFalse; -+ if (dataObject != NULL && *dataObject == NULL -+ && m_maxNestingDepth != 0) -+ { -+ *dataObject = new DcmDataset(); -+ (*dataObject)->setMaxNestingDepth(m_maxNestingDepth); -+ weAllocated = OFTrue; -+ } -+ - OFCondition cond; - /* call the corresponding DIMSE function to receive the dataset */ - if (m_progressNotificationMode) -@@ -2519,6 +2533,14 @@ - } - else - { -+ /* if we pre-allocated the dataset, clean up on error since -+ * DIMSE_receiveDataSetInMemory() won't delete a dataset -+ * that was passed in by the caller */ -+ if (weAllocated && dataObject != NULL) -+ { -+ delete *dataObject; -+ *dataObject = NULL; -+ } - OFString tempStr; - DCMNET_ERROR("Unable to receive dataset on presentation context " - << OFstatic_cast(unsigned int, *presID) << ": " << DimseCondition::dump(tempStr, cond)); -@@ -2607,6 +2629,11 @@ - m_progressNotificationMode = mode; - } - -+void DcmSCU::setMaxNestingDepth(const Sint32 maxDepth) -+{ -+ m_maxNestingDepth = maxDepth; -+} -+ - void DcmSCU::setProtocolVersion(T_ASC_ProtocolFamily protocolVersion) - { - m_protocolVersion = protocolVersion; -@@ -2694,6 +2721,11 @@ - return m_progressNotificationMode; - } - -+Sint32 DcmSCU::getMaxNestingDepth() const -+{ -+ return m_maxNestingDepth; -+} -+ - OFCondition DcmSCU::getDatasetInfo(DcmDataset* dataset, - OFString& sopClassUID, - OFString& sopInstanceUID, ---- dcmtk.orig/dcmnet/tests/tests.cc -+++ dcmtk/dcmnet/tests/tests.cc -@@ -51,6 +51,11 @@ - OFTEST_REGISTER(dcmnet_scu_sendNSETRequest_succeeds_and_modifies_instance_when_scp_has_instance); - OFTEST_REGISTER(dcmnet_scu_sendNSETRequest_succeeds_and_sets_responsestatuscode_from_scp_when_scp_sets_error_status); - -+OFTEST_REGISTER(dcmnet_scp_maxNestingDepth_getset); -+OFTEST_REGISTER(dcmnet_scu_maxNestingDepth_getset); -+OFTEST_REGISTER(dcmnet_scp_maxNestingDepth_rejects_deep); -+OFTEST_REGISTER(dcmnet_scp_maxNestingDepth_accepts_shallow); -+ - #endif // WITH_THREADS - - OFTEST_MAIN("dcmnet") ---- dcmtk.orig/dcmnet/tests/tscuscp.cc -+++ dcmtk/dcmnet/tests/tscuscp.cc -@@ -1113,4 +1113,209 @@ - } - - -+// Test that setMaxNestingDepth/getMaxNestingDepth work on DcmSCP -+OFTEST(dcmnet_scp_maxNestingDepth_getset) -+{ -+ DcmSCP scp; -+ // Default must be 0 (use compile-time default) -+ OFCHECK_EQUAL(scp.getMaxNestingDepth(), 0); -+ scp.setMaxNestingDepth(10); -+ OFCHECK_EQUAL(scp.getMaxNestingDepth(), 10); -+ scp.setMaxNestingDepth(-1); -+ OFCHECK_EQUAL(scp.getMaxNestingDepth(), -1); -+ scp.setMaxNestingDepth(0); -+ OFCHECK_EQUAL(scp.getMaxNestingDepth(), 0); -+} -+ -+ -+// Test that setMaxNestingDepth/getMaxNestingDepth work on DcmSCU -+OFTEST(dcmnet_scu_maxNestingDepth_getset) -+{ -+ DcmSCU scu; -+ // Default must be 0 (use compile-time default) -+ OFCHECK_EQUAL(scu.getMaxNestingDepth(), 0); -+ scu.setMaxNestingDepth(5); -+ OFCHECK_EQUAL(scu.getMaxNestingDepth(), 5); -+ scu.setMaxNestingDepth(-1); -+ OFCHECK_EQUAL(scu.getMaxNestingDepth(), -1); -+ scu.setMaxNestingDepth(0); -+ OFCHECK_EQUAL(scu.getMaxNestingDepth(), 0); -+} -+ -+ -+/** Helper: build a DcmDataset with nested sequences to a given depth. -+ * @param depth number of nesting levels -+ * @param dset output dataset (cleared before use) -+ */ -+static void buildNestedDataset(Uint32 depth, DcmDataset& dset) -+{ -+ dset.clear(); -+ DcmItem* currentItem = &dset; -+ for (Uint32 i = 0; i < depth; ++i) -+ { -+ DcmSequenceOfItems* sq = -+ new DcmSequenceOfItems(DCM_ReferencedSeriesSequence); -+ currentItem->insert(sq); -+ DcmItem* item = new DcmItem(); -+ sq->insert(item); -+ currentItem = item; -+ } -+} -+ -+ -+/** SCP that applies a nesting depth limit and records whether -+ * receiving the dataset succeeded or failed. -+ */ -+struct NestingTestSCP : TestSCP -+{ -+ NestingTestSCP(Sint32 maxNesting) -+ : TestSCP() -+ , m_receiveResult(EC_NotYetImplemented) -+ { -+ setMaxNestingDepth(maxNesting); -+ DcmSCPConfig& config = getConfig(); -+ config.setAETitle("NESTING_SCP"); -+ config.setConnectionBlockingMode(DUL_NOBLOCK); -+ config.setConnectionTimeout(10); -+ config.setHostLookupEnabled(OFFalse); -+ config.setPort(0); -+ OFList xfers; -+ xfers.push_back(UID_LittleEndianImplicitTransferSyntax); -+ OFCHECK(config.addPresentationContext( -+ UID_ModalityPerformedProcedureStepSOPClass, xfers).good()); -+ OFCHECK(openListenPort().good()); -+ m_portNum = config.getPort(); -+ } -+ -+ OFCondition handleIncomingCommand( -+ T_DIMSE_Message* incomingMsg, -+ const DcmPresentationContextInfo& presInfo) -+ { -+ if (incomingMsg->CommandField == DIMSE_N_CREATE_RQ) -+ { -+ T_DIMSE_N_CreateRQ& req = incomingMsg->msg.NCreateRQ; -+ if (req.DataSetType != DIMSE_DATASET_NULL) -+ { -+ DcmDataset* dataset = OFnullptr; -+ T_ASC_PresentationContextID presIDdset; -+ m_receiveResult = -+ receiveDIMSEDataset(&presIDdset, &dataset); -+ /* send a response regardless of receive outcome */ -+ T_DIMSE_Message rsp; -+ memset(&rsp, 0, sizeof(rsp)); -+ rsp.CommandField = DIMSE_N_CREATE_RSP; -+ T_DIMSE_N_CreateRSP& createRsp = rsp.msg.NCreateRSP; -+ createRsp.MessageIDBeingRespondedTo = req.MessageID; -+ createRsp.DimseStatus = m_receiveResult.good() -+ ? STATUS_N_Success -+ : STATUS_N_ProcessingFailure; -+ createRsp.DataSetType = DIMSE_DATASET_NULL; -+ OFStandard::strlcpy( -+ createRsp.AffectedSOPClassUID, -+ req.AffectedSOPClassUID, -+ sizeof(createRsp.AffectedSOPClassUID)); -+ createRsp.opts = O_NCREATE_AFFECTEDSOPCLASSUID; -+ sendDIMSEMessage( -+ presInfo.presentationContextID, &rsp, NULL); -+ delete dataset; -+ } -+ return EC_Normal; -+ } -+ return DcmSCP::handleIncomingCommand(incomingMsg, presInfo); -+ } -+ -+ /// Result of receiveDIMSEDataset() for the last request -+ OFCondition m_receiveResult; -+ /// Port the SCP is listening on -+ Uint16 m_portNum; -+}; -+ -+ -+// Test that SCP's nesting depth limit rejects deeply nested data -+// received over the network -+OFTEST_FLAGS(dcmnet_scp_maxNestingDepth_rejects_deep, EF_Slow) -+{ -+ NestingTestSCP scp(3); -+ scp.m_set_stop_after_assoc = OFTrue; -+ scp.start(); -+ OFStandard::forceSleep(1); -+ -+ // SCU sends a dataset with 4 levels of nesting (exceeds limit 3) -+ DcmSCU scu; -+ scu.setAETitle("NESTING_SCU"); -+ scu.setPeerAETitle("NESTING_SCP"); -+ scu.setPeerHostName("localhost"); -+ scu.setPeerPort(scp.m_portNum); -+ OFList xfers; -+ xfers.push_back(UID_LittleEndianImplicitTransferSyntax); -+ OFCHECK(scu.addPresentationContext( -+ UID_ModalityPerformedProcedureStepSOPClass, xfers).good()); -+ OFCHECK(scu.initNetwork().good()); -+ OFCHECK(scu.negotiateAssociation().good()); -+ -+ DcmDataset reqDataset; -+ buildNestedDataset(4, reqDataset); -+ T_ASC_PresentationContextID presID = scu.findPresentationContextID( -+ UID_ModalityPerformedProcedureStepSOPClass, -+ UID_LittleEndianImplicitTransferSyntax); -+ OFCHECK(presID != 0); -+ Uint16 rspStatus = 0; -+ DcmDataset* rspDataset = NULL; -+ scu.sendNCREATERequest(presID, "1.2.3.4.5", -+ &reqDataset, rspDataset, rspStatus); -+ delete rspDataset; -+ if (scu.isConnected()) -+ scu.releaseAssociation(); -+ OFStandard::forceSleep(2); -+ scp.join(); -+ -+ // Verify SCP saw the nesting depth error -+ OFCHECK(scp.m_receiveResult == DIMSE_RECEIVEFAILED); -+} -+ -+ -+// Test that SCP's nesting depth limit accepts data within the limit -+OFTEST_FLAGS(dcmnet_scp_maxNestingDepth_accepts_shallow, EF_Slow) -+{ -+ NestingTestSCP scp(3); -+ scp.m_set_stop_after_assoc = OFTrue; -+ scp.start(); -+ OFStandard::forceSleep(1); -+ -+ // SCU sends a dataset with 3 levels of nesting (at the limit) -+ DcmSCU scu; -+ scu.setAETitle("NESTING_SCU"); -+ scu.setPeerAETitle("NESTING_SCP"); -+ scu.setPeerHostName("localhost"); -+ scu.setPeerPort(scp.m_portNum); -+ OFList xfers; -+ xfers.push_back(UID_LittleEndianImplicitTransferSyntax); -+ OFCHECK(scu.addPresentationContext( -+ UID_ModalityPerformedProcedureStepSOPClass, xfers).good()); -+ OFCHECK(scu.initNetwork().good()); -+ OFCHECK(scu.negotiateAssociation().good()); -+ -+ DcmDataset reqDataset; -+ buildNestedDataset(3, reqDataset); -+ T_ASC_PresentationContextID presID = scu.findPresentationContextID( -+ UID_ModalityPerformedProcedureStepSOPClass, -+ UID_LittleEndianImplicitTransferSyntax); -+ OFCHECK(presID != 0); -+ Uint16 rspStatus = 0; -+ DcmDataset* rspDataset = NULL; -+ OFCondition result = scu.sendNCREATERequest(presID, "1.2.3.4.5", -+ &reqDataset, rspDataset, rspStatus); -+ OFCHECK_MSG(result.good(), result.text()); -+ OFCHECK(rspStatus == STATUS_N_Success); -+ delete rspDataset; -+ if (scu.isConnected()) -+ OFCHECK(scu.releaseAssociation().good()); -+ OFStandard::forceSleep(2); -+ scp.join(); -+ -+ // Verify SCP successfully received the dataset -+ OFCHECK(scp.m_receiveResult.good()); -+} -+ -+ - #endif // WITH_THREADS diff --git a/debian/patches/series b/debian/patches/series index 56c3d886..910203c1 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -5,4 +5,3 @@ remove_version.patch skip-bigendian-roundtrip-failure.patch hurd.patch CVE-2026-5663.patch -CVE-2026-10528-partial.patch -- 2.39.5